import * as fs from "fs"
import { BufferContainer} from "./Buff";

// 
let inFile = "";
// let outFile = new TypedArrayList(Int16Array, 32);
let outFile = new BufferContainer();

// 
let pcoffset9 = 0, pcoffset11 = 0;
let imm5 = 0, imm9 = 0, offset6 = 0;
let macword = 0, addr = 0;
let dr = 0, sr = 0, sr1 = 0, sr2 = 0;
let baser = 0, trapvect8 = 0;

let outFileName = "";
let lineSave = "";
let buf = "";

let p = "", mnemonic = "", o1 = "", o2 = "", o3 = "0", label = "";

let num = 0, linenum = 0, rc = 0, loc_ctr = 0;

let regNames = ["r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7"];
let symbolTable: Map<string, number> = new Map();
function error(...msg: string[]) {
    console.log("err", msg)
    throw new Error("error");
}
function lineToArray(line: string) {
    let result = [];
    let lineArray = line.split(/[ \n\r\t]/)
        .filter(t => t)
        .map(t => {
            return t.replace(",", "")
        })
    // 指令后的注释
    for (let i = 0; i < lineArray.length; i++) {
        if (lineArray[i].indexOf(";") > -1) {
            result.push(lineArray[i].replace(";", ""))
            break;
        } else {
            result.push(lineArray[i]);
        }
    }
    return result;
}
function isReg(oprand: string) {
    return regNames.indexOf(oprand) > -1;
}
/**
 * 获取寄存器编号
 * @param regName 
 * @returns 
 */
function getReg(regName: string) {
    if (regNames.indexOf(regName) > -1) {
        return parseInt(regName.replace("r", ""))
    }
    error("getReg错误", lineSave, regName);
}
/**
 * 获取label的地址
 * @param label 
 * @returns 
 */
function getAddr(label: string) {
    let addr = symbolTable.get(label);
    if (addr) {
        return addr
    }
    error("getAddr 符号查找失败", lineSave, label);
}

function main(inFileName: string, outFileName: string = "") {
    // 读取文件
    if (!inFileName) {
        console.log("请输入需要汇编的文件名")
        console.log("Usage:ts-node a1.ts <input filename>")
        return;
    }

    console.log("1. lcc开始汇编:", new Date().toLocaleString())
    if (!fs.existsSync(inFileName)) {
        console.log("error", `汇编文件${inFileName}不存在`)
        return;
    }
    if (!outFileName || outFileName.indexOf('.e') == -1) {
        outFileName = inFileName.split('.')[0] + ".e";
    }

    inFile = fs.readFileSync(inFileName, "utf-8");
    // 默认开始 第0行
    // outFile.pushU16("C".charCodeAt(0));
    // console.log("out", outFile);
    console.log("开始Pass 1")
    loc_ctr = linenum = 0;

    let lines = inFile.split("\r\n");
    while (linenum < lines.length) {

        linenum = linenum + 1;
        lineSave = lines[linenum];

        if (!lineSave) {
            continue;
        }
        if (lineSave[0] !== " " && lineSave[0] !== ":") { //label开始的
            let temps = lineToArray(lineSave)
            // label
            symbolTable.set(temps[0].replace(":", ""), loc_ctr);
            // 指令/伪指令
            mnemonic = temps[1];
            // 参数1
            o1 = temps[2];
        } else { //没有标签的
            if (lineSave.trim()[0] == ";") {
                // 注释行
                continue;
            }
            let temps = lineToArray(lineSave.trim())
            // 指令/伪指令
            mnemonic = temps[0];
            // 参数1
            o1 = temps[1];
        }

        if (!mnemonic) {
            continue;
        }
        // .word 填充当前word为常量
        if (mnemonic == ".zero") {
            if (o1) {
                let o1Num = parseInt(o1);
                // 判定范围
                if (o1Num < 1 || o1Num > (65536) - loc_ctr) {
                    error(".zero参数过大", o1);
                    break
                }
                loc_ctr = loc_ctr + o1Num;
            } else {
                error(".zero参数缺少");
                break
            }
        } else {
            loc_ctr = loc_ctr + 1;
        }
        if (loc_ctr > 65536) {
            error("超出内存", loc_ctr.toString())
        }
    }
    console.log("symTable", symbolTable)

    console.log("开始 Pass2");
    loc_ctr = linenum = 0;
    while (linenum < lines.length) {
        linenum = linenum + 1;
        lineSave = lines[linenum];

        if (lineSave == undefined || lineSave == "" || lineSave == ";") {
            continue;
        }
        console.log(loc_ctr, "-->", lineSave);

        if (lineSave[0] !== " ") { //label开始的
            let temps = lineToArray(lineSave)
            // label
            symbolTable.set(temps[0], loc_ctr);
            // 指令/伪指令
            mnemonic = temps[1];
            // 参数1
            o1 = temps[2] ? temps[2].replace(",", "") : "";
            o2 = temps[3] ? temps[3].replace(",", "") : "";
            o3 = temps[4] ? temps[4].replace(",", "") : "";

        } else { //没有标签的
            let temps = lineToArray(lineSave.trim())
            // 指令/伪指令
            mnemonic = temps[0];
            // 参数1
            o1 = temps[1] ? temps[1].replace(",", "") : "";
            o2 = temps[2] ? temps[2].replace(",", "") : "";
            o3 = temps[3] ? temps[3].replace(",", "") : "";
        }
        if (mnemonic == undefined || mnemonic == " ") {
            continue;
        }
        // 指令解析
        // console.log("mnemonic",mnemonic)
        // br**
        if (mnemonic[0] == "b" && mnemonic[1] == "r") {
            if (mnemonic == "brz") {
                macword = 0x0000
            } else if (mnemonic == "brnz") {
                macword = 0x0200
            } else if (mnemonic == "brn") {
                macword = 0x0400
            } else if (mnemonic == "brp") {
                macword = 0x0600
            } else if (mnemonic == "brlt") {
                macword = 0x0800
            } else if (mnemonic == "brgt") {
                macword = 0x0a00
            } else if (mnemonic == "brc") {
                macword = 0x0c00
            } else if (mnemonic == "br") {
                macword = 0x0e00
            } else {
                error("无效的br指令", mnemonic)
            }
            // console.log("br mac",macword,mnemonic)
            pcoffset9 = (getAddr(o1) as number) - loc_ctr - 1;
            if (pcoffset9 > 255 || pcoffset9 < -256) {
                error("pcoffset9超出范围", pcoffset9.toString())
            }
            macword = macword | (pcoffset9 & 0x1ff);
            // console.log("br",macword)
            outFile.pushU16(macword);
            loc_ctr = loc_ctr + 1;
        } else if (mnemonic == "add") {
            if (o3 == "") {
                error("add参数缺少", o1, o2, o3)
            }
            dr = getReg(o1) as number << 9;
            sr1 = getReg(o2) as number << 6;

            if (isReg(o3)) {
                sr2 = getReg(o3) as number;
                macword = 0x1000 | dr | sr1 | sr2;
            } else {
                // imm5 
                let imm5Num = parseInt(o3);
                if (imm5Num > 15 || imm5Num < -16) {
                    error("imm5参数错误", imm5Num.toString())
                }
                macword = 0x1000 | dr | sr1 | 0x0020 | (imm5Num & 0x1f)
            }
            // console.log("add",macword)
            outFile.pushU16(macword);
            loc_ctr = loc_ctr + 1;
        } else if (mnemonic == "ld") {
            dr = getReg(o1) as number << 9;
            pcoffset9 = (getAddr(o2) as number - loc_ctr - 1);
            if (pcoffset9 > 255 || pcoffset9 < -256) {
                error("pcoffset error", pcoffset9.toString());
            }
            macword = 0x2000 | dr | (pcoffset9 & 0x1ff);
            console.log("ld",macword.toString(2),dr,pcoffset9,loc_ctr,o2)
            outFile.pushU16(macword);
            loc_ctr = loc_ctr + 1;
        } else if (mnemonic == "st") {
            sr = getReg(o1) as number << 9;
            pcoffset9 = (getAddr(o2) as number - loc_ctr - 1);

            if (pcoffset9 > 255 || pcoffset9 < -256) {
                error("pcoffset error", pcoffset9.toString());
            }
            macword = 0x3000 | sr | (pcoffset9 & 0x1ff);
            console.log("st",macword,sr,pcoffset9)
            outFile.pushU16(macword);
            loc_ctr = loc_ctr + 1;

        } else if (mnemonic == "bl") {
            pcoffset11 = getAddr(o1) as number - loc_ctr - 1;
            macword = 0x4000 | 1 << 11 | (pcoffset11 & 0x7ff)
            // console.log("bl",macword,pcoffset11)
            outFile.pushU16(macword);
            loc_ctr = loc_ctr + 1;
        } else if (mnemonic == "blr") {
            baser = getReg(o1) as number;
            offset6 = parseInt(o3);
            macword = 0x4000 | baser << 6 | (offset6 & 0xff)
            console.log("blr",macword,pcoffset11)
            outFile.pushU16(macword);
            loc_ctr = loc_ctr + 1;
        } else if (mnemonic == "and") {
            dr = getReg(o1) as number;
            sr1 = getReg(o2) as number;
            if (isReg(o3)) {
                sr2 = getReg(o3) as number
                macword = 0x5000 | dr << 9 | sr1 << 6 | sr2
            } else {
                let imm5Num = parseInt(o3);
                if (imm5Num > 15 || imm5Num < -16) {
                    error("imm5参数错误", imm5Num.toString())
                }
                macword = 0x5000 | dr << 9 | sr1 << 6 | 1 << 5 | imm5Num
            }
            // console.log("and",lineSave,macword)
            outFile.pushU16(macword);
            loc_ctr = loc_ctr + 1;
        } else if (mnemonic == "ldr") {
            dr = getReg(o1) as number;
            baser = getReg(o2) as number;
            offset6 = parseInt(o3);

            macword = 0x6000 | dr << 9 | baser << 6 | offset6
            // console.log("ldr",macword)
            outFile.pushU16(macword);
            loc_ctr = loc_ctr + 1;
        } else if (mnemonic == "str") {
            sr = getReg(o1) as number;
            baser = getReg(o2) as number;
            offset6 = parseInt(o3);

            macword = 0x7000 | sr << 9 | baser << 6 | offset6
            // console.log("str",macword)
            outFile.pushU16(macword);
            loc_ctr = loc_ctr + 1;
        } else if (mnemonic == "not") {
            dr = getReg(o1) as number;
            sr1 = getReg(o2) as number;

            macword = 0x9000 | dr << 9 | sr1 << 6;
            // console.log("not",macword);
            outFile.pushU16(macword);
            loc_ctr = loc_ctr + 1;
        } else if (mnemonic == "jmp") {
            baser = getReg(o1) as number;
            offset6 = o2 == "" ? 0 : parseInt(o2);
            // jmp 指令说明错误
            macword = 0xc000 | 7 << 9 | baser << 6 | offset6;
            // console.log("jmp",offset6,baser,macword);
            outFile.pushU16(macword);
            loc_ctr = loc_ctr + 1;
        } else if (mnemonic == "ret") {
            offset6 = o1 == "" ? 0 : parseInt(o1);
            // ret 指令说明错误
            macword = 0xc000 | 7 << 9 | 7 << 6 | offset6;
            // console.log("ret",offset6,macword);
            outFile.pushU16(macword);
            loc_ctr = loc_ctr + 1;
        } else if (mnemonic == "lea") {
            dr = getReg(o1) as number;
            pcoffset9 = (getAddr(o2) as number - loc_ctr - 1);

            macword = 0xe000 | dr << 9 | pcoffset9;
            console.log("lea",macword,dr,pcoffset9)
            outFile.pushU16(macword);
            loc_ctr = loc_ctr + 1;
        } else if (mnemonic == "halt") {
            macword = 0xf000
            // console.log("halt", macword)
            outFile.pushU16(macword);
            loc_ctr = loc_ctr + 1;
        } else if (mnemonic == "nl") {
            macword = 0xf001
            // console.log("nl", macword)
            outFile.pushU16(macword);
            loc_ctr = loc_ctr + 1;
        } else if (mnemonic == "dout") {
            // console.log("o1", lineToArray(lineSave.trim()));
            sr = o1 == "" ? 0 : getReg(o1) as number;
            macword = 0xf000 | sr << 9 | 2
            // console.log("dout", macword);
            outFile.pushU16(macword);
            loc_ctr = loc_ctr + 1;
        } else if (mnemonic == ".word") {
            macword = parseInt(o1);
            // console.log(".word", macword);
            outFile.pushU16(macword & 0xffff);
            loc_ctr = loc_ctr + 1;
        } else if (mnemonic == ".zero") {
            macword = 0
            let o1Num = parseInt(o1);
            // tempO1用来填充内存 保证o1Num的正确
            let tempO1 = o1Num
            while (tempO1) {
                outFile.pushU16(macword);
                tempO1 = tempO1 - 1;
            }
            loc_ctr = loc_ctr + o1Num;
        }
    }
    
    let i16v = new Int16Array(outFile.buffer);
    let outBuffer = new BufferContainer();
    // 添加前2个字节
    outBuffer.pushU8('o'.charCodeAt(0))
    outBuffer.pushU8('C'.charCodeAt(0))
    let header = outBuffer.readI16(0);
    // 输出的内容
    let outI16 = new Int16Array(i16v.length+1);
    outI16[0] = header;
    outI16.set(i16v,1);
    console.log("out",outI16);
    fs.writeFileSync(outFileName,outI16)
    console.log("ok");
}
function a1() {
    // buf = `add r0,r1,r2`
    // getReg(2);
    main("test.a");
}
export {
    a1
}